/* Copyright (c) 2014-2015 Diego Ongaro
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "Client/Backoff.h"

namespace LogCabin {
namespace Client {

Backoff::Backoff(uint64_t windowCount, uint64_t windowNanos)
    : mutex()
    , windowCount(std::max(1UL, windowCount))
    , windowDuration(windowNanos)
    , startTimes()
{
    for (uint64_t i = 0; i < windowCount; ++i)
        startTimes.push_back(TimePoint::min());
}

Backoff::~Backoff()
{
}

void
Backoff::delayAndBegin(TimePoint timeout)
{
    std::lock_guard<std::mutex> lockGuard(mutex);
    TimePoint now = Clock::now();
    if (now > timeout)
        return;
    TimePoint oldest = startTimes.at(0);
    TimePoint permissible = oldest + windowDuration;
    if (permissible > now) {
        if (permissible > timeout) { // now < timeout < permissible
            Core::Time::sleep(timeout);
            return;
        } else { // now < permissible < timeout
            Core::Time::sleep(permissible);
            now = Clock::now();
        }
    }
    startTimes.pop_front();
    startTimes.push_back(now);
}

} // namespace LogCabin::Client
} // namespace LogCabin
